#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import json
import os
import shutil
import tkinter as tk
import uuid
from pathlib import Path

import util_theme
import utils
from m_widget import FileChooser, Start


class Main:
    title = "TL Packer v1.0.0.3"
    count = 0
    has_query = False

    def __init__(self):
        self.win = None
        self.fc_input = None
        self.fc_out = None
        self.start = None
        self.t1 = ""
        self.ffmpeg_path = ""
        self.log_path = ""
        self.failure_list = []
        import sys
        argv = sys.argv.copy()
        self.exe_parent = Path(Path(argv[0]).parent)

    def run_func(self):
        """启动函数
        """
        self.setup()
        self.setup_after()

    def setup(self):
        """创建 ui
        """
        # tk 构建开始
        win = tk.Tk()
        win.title(self.title)
        utils.win = win
        self.win = win

        util_theme.init_style()

        # %USERPROFILE%\Documents\My Games\Terraria\ResourcePacks
        dc = self.read_setting()
        if dc["input_dir"] != "":
            init_dir = dc["input_dir"]
        else:
            init_dir = os.path.join(os.environ['USERPROFILE'], 'Documents', 'My Games', 'Terraria', 'ResourcePacks')
        fc_input = FileChooser(
            win,
            label_text="输入目录",
            action_btn_text='选择',
            action_btn_call=self.record_dir,
            isFolder=True,
            hasGROOVE=True,
            text_width=75
        )
        fc_input.set_text(init_dir)
        fc_input.grid(column=1, row=3, sticky=tk.W)

        if dc["output_dir"] != "":
            init_dir = dc["output_dir"]
        else:
            init_dir = os.path.join(os.environ['USERPROFILE'], 'Desktop')
        fc_out = FileChooser(
            win,
            label_text="导出目录",
            action_btn_text='选择',
            action_btn_call=self.record_dir,
            isFolder=True,
            hasGROOVE=True,
            text_width=75
        )
        fc_out.set_text(init_dir)
        fc_out.grid(column=1, row=4, sticky=tk.W)

        self.fc_input = fc_input
        self.fc_out = fc_out
        self.start = Start(win, text=' 开始 ', command=self.start_check)
        self.start.grid(column=1, row=5, sticky=tk.W)

    def setup_after(self):
        """ui 设置完成
        """
        self.win.mainloop()

    def start_check(self):
        if self.has_query:
            utils.showinfo('已有任务在处理，请稍等…')
            return
        self.has_query = True

        p1 = Path(self.fc_input.get_text())
        p2 = Path(self.fc_out.get_text())

        # icon.png
        boo, np = self.process_desc(p1, p2)
        if not boo:
            self.has_query = False
            utils.showinfo("找不到 资源包图标，转换已停止！")
            return
        else:
            self.start.update_query("已处理  icon.png")
        p2 = np

        # pack.json
        boo = self.process_icon(p1, p2)
        if not boo:
            self.has_query = False
            utils.showinfo("找不到 资源包描述文件，转换已停止！")
            return
        else:
            self.start.update_query("已处理  pack.json")

        # 禁用开始按钮
        self.start.clear_query()
        self.lock_btn(True)

        seq = ('p1`', "p2")
        dc = dict.fromkeys(seq, "")
        dc["p1"] = p1
        dc["p2"] = p2
        self.count = 0

        # 执行操作
        import threading
        self.t1 = threading.Thread(target=self.process, args=(dc, ''))
        self.t1.setDaemon(True)
        self.t1.start()

    def process(self, dc, astr=''):
        p1 = dc["p1"]
        p2 = dc["p2"]
        # 处理资源文件
        file_list = []
        images_path = str(Path(p1, "Content"))
        for main_dir, sub_dir, file_name_list in os.walk(p1):
            for filename in file_name_list:
                p = Path(main_dir, filename)
                if not str(p).count(images_path):
                    continue
                file_list.append(p)

        self.process_file(file_list, p1, p2)

        self.start.update_query("处理完成！")
        self.has_query = False
        self.t1 = ""
        self.lock_btn(False)
        utils.open_dir(str(p2))

    def process_file(self, file_list, raw_path, new_path):
        self.log_path = Path(new_path, "log.txt")
        if self.log_path.exists():
            os.remove(str(self.log_path))
        self.failure_list = []

        new_path = Path(os.path.join(new_path, "Modified"))
        if not new_path.exists():
            os.makedirs(new_path)

        # 先分类
        # Content/Images/UI/
        prefix = "Images" + os.sep
        gui_tags = [
            prefix + "Buff_",
            prefix + "Inventory_",
            prefix + "Heart",
            prefix + "House_",
            prefix + "NPC_Head_",
            prefix + "UI"
        ]
        img_list = []
        gui_list = []
        audio_list = []
        au_trans_list = []

        pass_list = []
        for p in file_list:
            suffix = p.suffix.lower()
            if suffix == ".png":
                has_tag = False
                for tag in gui_tags:
                    has_tag = str(p).count(tag)
                    if has_tag:
                        break
                if has_tag:
                    gui_list.append(p)
                else:
                    img_list.append(p)
            elif suffix == ".mp3":
                audio_list.append(p)
            elif suffix == ".wav":
                au_trans_list.append(p)
            elif suffix == ".ogg":
                au_trans_list.append(p)
            else:
                pass_list.append(p)

        def get_entry_name(_p):
            return str(_p).replace(str(raw_path) + os.sep, "").replace("\\", "/")

        # 逐个处理
        for p in img_list:
            self.count += 1
            self.show_query("{0}  {1}".format(self.count, get_entry_name(p)))
            self.create_texture(p, raw_path, new_path)

        for p in gui_list:
            self.count += 1
            self.show_query("{0}  {1}".format(self.count, get_entry_name(p)))
            self.create_texture(p, raw_path, new_path, True)

        for p in audio_list:
            self.count += 1
            self.show_query("{0}  {1}".format(self.count, get_entry_name(p)))
            self.create_audio(p, raw_path, new_path)

        if len(au_trans_list):
            p = Path(os.path.join(str(self.exe_parent), 'bin', "ffmpeg.exe"))
            if p.exists():
                self.ffmpeg_path = p
                for p in au_trans_list:
                    self.count += 1
                    self.show_query("{0}  {1}".format(self.count, get_entry_name(p)))
                    self.create_audio_trans(p, raw_path, new_path)
            else:
                self.show_query("----------")
                self.show_query("因缺少 ffmpeg组件，这部分文件未被转码：")
                self.show_query("（将ffmpeg.exe拷贝到本工具的相同目录下，将会自动转码）")
                for p in au_trans_list:
                    self.show_query("已跳过   {0}".format(get_entry_name(p)))
                    self.show_query("----------")
        for p in self.failure_list:
            self.show_query("转码失败   {0}".format(get_entry_name(p)))
        for p in pass_list:
            self.show_query("已跳过   {0}".format(get_entry_name(p)))

    def show_query(self, _s, _type=0):
        # _type  0 all, 1 update_query, 2 write_log
        self.start.update_query(_s)
        utils.write_log(str(self.log_path), utils.get_hms() + " " + _s)

    def create_texture(self, file_path, raw_path, new_path, is_ui=False):
        # Content/Images/DukeFishron
        jf = json.loads(
            '''{"entry_name": "",
                "number_of_horizontal_frames": 1,
                "milliseconds_per_frame": 16
                }'''
        )
        p = Path(new_path, str(self.count))
        if is_ui:
            ext = ".gui"
        else:
            ext = ".texture"
        shutil.copyfile(str(file_path), str(p.with_suffix(ext)))

        p2 = Path(new_path, str(self.count) + ".json")
        jf["entry_name"] = str(file_path.with_suffix("")).replace(str(raw_path) + os.sep, "").replace("\\", "/")
        utils.write_txt(str(p2), json.dumps(jf, ensure_ascii=False, indent=2))

    def create_audio(self, file_path, raw_path, new_path):
        # "entry_name": "Content/Music/Music_46"
        jf = json.loads('''{"entry_name": ""}''')

        p = Path(new_path, str(self.count))
        shutil.copyfile(str(file_path), str(p.with_suffix(".audio")))

        p2 = Path(new_path, str(self.count) + ".json")
        jf["entry_name"] = str(file_path.with_suffix("")).replace(str(raw_path) + os.sep, "").replace("\\", "/")
        utils.write_txt(str(p2), json.dumps(jf, ensure_ascii=False, indent=2))

    def create_audio_trans(self, file_path, raw_path, new_path):
        # "entry_name": "Content/Music/Music_46"
        jf = json.loads('''{"entry_name": ""}''')

        def get_entry_name(_p):
            return str(_p).replace(str(raw_path) + os.sep, "").replace("\\", "/")

        p = Path(new_path, str(self.count))
        p_out = p.with_suffix(".mp3")
        self.show_query("   正在转码……")
        self.trans_audio(file_path, str(p_out))
        if not p_out.exists():
            self.count -= 1
            self.failure_list.append(file_path)
            self.show_query("   转码失败 ;-(")
            return

        self.show_query("   转码成功 ;-)")
        # 改名
        p_out2 = p_out.with_suffix(".audio")
        if p_out2.exists():
            os.remove(str(p_out2))
        os.rename(str(p_out), str(p_out2))
        # shutil.copyfile(str(file_path), str(p.with_suffix(".audio")))

        p2 = Path(new_path, str(self.count) + ".json")
        jf["entry_name"] = str(file_path.with_suffix("")).replace(str(raw_path) + os.sep, "").replace("\\", "/")
        utils.write_txt(str(p2), json.dumps(jf, ensure_ascii=False, indent=2))

    def trans_audio(self, input_file, output_file):
        param = 'ffmpeg -y -i "{in_file}" ' \
                '-acodec libmp3lame -ac 2 -ar 44100 -b:a 128k -hide_banner -loglevel quiet "{out_file}"'
        param = param.format(in_file=input_file, out_file=output_file)
        self.execute(param)

    def execute(self, command_str):
        """执行 ffmpeg 命令
        例如：ffmpeg -i input.mp4 output.mp4
        """
        s = str(self.ffmpeg_path) + command_str[6:len(command_str)]
        # print("执行命令行:", s)
        try:
            utils.execute(s)
        except PermissionError:
            print("PermissionError")
        else:
            pass

    def process_icon(self, raw_path, new_path):
        p = Path(os.path.join(raw_path, "icon.png"))
        if not p.exists():
            p = Path(os.path.join(raw_path, "icon.jpg"))
        if not p.exists():
            return False
        shutil.copyfile(p, os.path.join(new_path, p.name))
        return True

    def process_desc(self, raw_path, new_path):
        p1 = Path(os.path.join(raw_path, "pack.json"))
        if not p1.exists():
            return False, ""
        f = open(p1, 'rb')
        jf1 = json.load(f, strict=False)
        new_path = Path(new_path, self.legal_file_name(jf1["Name"]))
        self.start.update_query("资源包名字:《{0}》".format(jf1["Name"]))
        if not new_path.exists():
            os.makedirs(new_path)

        p2 = Path(os.path.join(new_path, "Settings.json"))
        jf2 = json.loads(
            '''{"packStructureVersion": 17,
               "title": "",
               "guid": "",
               "descriptionRussian": "",
               "descriptionEnglish": "",
               "authors": [
                   {
                       "name": "",
                       "color": "#ffffffff",
                       "link": "",
                       "icon_height": 128
                   }
               ],
               "version": 1}'''
        )

        jf2["title"] = jf1["Name"]
        jf2["authors"][0]["name"] = jf1["Author"]
        jf2["descriptionEnglish"] = jf1["Description"].replace("\r\n", "<br>").replace("\n", "<br>")
        jf2["version"] = self.get_ver_num(jf1["Version"]["major"], jf1["Version"]["minor"])
        jf2["guid"] = str(uuid.uuid1())
        utils.write_txt(str(p2), json.dumps(jf2, ensure_ascii=False, indent=2))
        return True, new_path

    def lock_btn(self, is_lock):
        self.has_query = is_lock
        enable = False if is_lock else True
        self.start.set_state(enable)

    def record_dir(self):
        dc = self.read_setting()
        dc["input_dir"] = self.fc_input.get_text()
        dc["output_dir"] = self.fc_out.get_text()
        self.save_setting(dc)

    def read_setting(self):
        """从 json 文件中读取设置
        """
        setting_file = Path(self.exe_parent, "config.ini")
        seq = ('output_dir', 'input_dir')
        if setting_file.exists():
            f = open(str(setting_file), 'rb')
            jf = json.load(f)

            # 同步键值
            dc = jf.copy()
            for s in seq:
                if s not in jf:
                    dc.setdefault(s, "")
        else:
            dc = dict.fromkeys(seq, "")
        return dc

    def save_setting(self, dc):
        """保存设置到json文件
        bool 类型用字符串 1 或 0来表示
        """
        setting_file = Path(self.exe_parent, "config.ini")
        s = json.dumps(dc, ensure_ascii=False, indent=2)
        utils.write_txt(str(setting_file), s)

    @staticmethod
    def get_ver_num(major, minor):
        m = int(major) * 100 + int(minor)
        return str(m)

    @staticmethod
    def legal_file_name(f):
        f = f.replace(" ", "_").replace(":", "_").replace("\t", "_").replace("'", "").replace('"', "").replace('\\', "").replace('/', "")
        return f


if __name__ == "__main__":
    g = Main()
    g.run_func()

"""
.[材质包的guid]
|-- Modified
|   |-- 1.texture
|   |-- 1.json
|   |-- 2.gui
|   |-- 2.json
|   |-- 3.audio
|   |-- 3.json
|   |-- 4.font
|   `-- 4.json
|-- Authors
|   `-- 1.png
|-- Previews
|   `-- 1.png
|-- Icon.png
`-- Settings.json
"""
